#include<ncurses.h>
#include<stdlib.h>
#include<time.h>
#include<pthread.h>
#include<stdlib.h>


#define RIGHT 1
#define LEFT -1
#define UP    2
#define DOWN -2


#define ROW 	20
#define COL 	20
#define Speed 	100000  //The smaller the value, the faster;

struct Snake{
	
	int Row;
	int Col;
	struct  Snake* next;
		
};

struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
int dir = 0;
int con = 0;
int score = 0;

void Initfood();
int havefood();
void show();


int getNode(int i,int j){

	struct Snake *p = NULL;
	p = head;
	while(p != NULL){
		if(p-> Row == i && p-> Col ==j ){
			return 1;
		}
		p = p->next;
	}
	return 0;
}

void InitSnakeBoard(){

	clear();
	int i = 0,j = 0;
	for(i = 0;i < ROW; i++){
		
		for(j = 0; j <= COL; j++){
			
			if(i == 0 || i == ROW-1){
				
				if(j < COL)
				printw("--");
			}
			else if(i > 0 && i <= ROW-1){

				if(j == 0 || j == COL){

					printw("|");
				}
				else{
					if(getNode(i,j)){
						
						printw("[]");
					}
					else if(havefood(i,j)){
						printw("##");
					}
					else
					printw("  ");
				}
			}
		
		}
	printw("\n");
	}
}

void  CreatNode(){

	struct Snake *new = NULL;
	new = (struct Snake*)malloc(sizeof(struct Snake));
	
	switch(dir){

	case RIGHT:
		new->  Row  = tail-> Row;
		new->  Col  = tail-> Col+1;
		new->  next = NULL;
		break;
	case LEFT:
		new->  Row  = tail-> Row;
        	new->  Col  = tail-> Col-1;
        	new->  next = NULL;
		break;
	case UP:
		new->  Row  = tail-> Row-1;
        	new->  Col  = tail-> Col;
        	new->  next = NULL;
		break;
	case DOWN:
		new->  Row  = tail-> Row+1;
        	new->  Col  = tail-> Col;
        	new->  next = NULL;
		break;
}
	
	tail-> next = new;
	tail = new; 
}

void DeletNode(){
	
	if(head == NULL)
		return;
	struct Snake *delet = NULL;
	delet = head ;
	head = head-> next;
	free(delet);
	
}

void InitSnake(){

	dir = RIGHT;
	score = 0;
	while(head != NULL){
	
	struct Snake *temp = head;
        head = head->next;
        free(temp);
	}
	
	head = (struct Snake*)malloc(sizeof(struct Snake));
        tail = (struct Snake*)malloc(sizeof(struct Snake));

        head-> Row  = 2;
        head-> Col  = 2;
        head-> next = NULL;

        tail = head;
	Initfood();	
	CreatNode();
	CreatNode();
	
}

int havefood(i,j){
	
	if(food.Row == i && food.Col == j){

		return 1;
	}
	else
		return 0;
}

void Initfood(){
	
	int f = 1;
	while(f){
		
		int row = rand()%(ROW-2)+1;
                int col = rand()%(COL-2)+1;

		if(!getNode(row,col)){
				
			food.Row = row;
			food.Col = col;
			f = 0;
		}	
	}	
}

int ifSnakeDie(){

	struct Snake *p = head;
	
	if(tail-> Row == 0 ||tail-> Row == ROW-1 || tail->Col == 0 || tail-> Col == COL){
		return 1;
	}

	while(p->next != NULL){
		if(p-> Row == tail-> Row && p-> Col == tail-> Col){
			return 1;
		}
		p = p->next;
	}
	
	return 0;
	
}

void *InitSnakeMove(){

    while(1){	
	CreatNode();	
	if(havefood(tail->Row,tail->Col)){
		CreatNode();
		Initfood();
		score++;
	}
	else{
		DeletNode();
	}
	move(0,0);
	if(ifSnakeDie()){
                        InitSnake();
                }

	InitSnakeBoard();
	show();
	refresh();
	usleep(Speed);
	  
    } 

}

void turn(int tn){

	if(abs(tn) != abs(dir)){
		dir = tn;
	}
}


void *KeyBoardMove(){



	while(1){
		con = getch();
		switch(con){
			
			case KEY_RIGHT:
				turn(RIGHT);					
					break;
			case KEY_LEFT:
				turn(LEFT);
					break;
			case KEY_UP:
				turn(UP);
					break;
			case KEY_DOWN:
				turn(DOWN);
					break;
			default:	
				printw("input error\n");
				continue;
			
		}
	} 

}
void Initncurse()

{
	initscr();
        keypad(stdscr, 1);
        noecho();
        cbreak();

}

void show(){

	move(ROW, 0);
    	printw("game by zxl,Food(%d, %d),Score: %d", food.Row, food.Col, score);
}

int main()

{	
 	Initncurse();	
	pthread_t t1;
	pthread_t t2;

	srand((unsigned int)time(NULL));

	InitSnake();
	InitSnakeBoard();

	pthread_create(&t1,NULL,InitSnakeMove,NULL);
	pthread_create(&t2,NULL,KeyBoardMove,NULL);
	while(1);
	while (head != NULL) {
     		struct Snake *temp = head;
        	head = head->next;
        	free(temp);
    	}	

	endwin();	
	return 0;
}
